#include "stdafx.h"

#pragma comment(lib,"gdiplus")

using namespace Gdiplus;

const int XWINDOW = 350;
const int YWINDOW = 630;
int XSCREEN, YSCREEN;
const Color BACKCOLOR = 0x33aaaaaa;
const int BUTTONWIDTH = 31;
const int x = 20, y = 60;

struct GdipInit
{
	GdipInit()
	{
		GdiplusStartupInput gsi;
		ULONG_PTR token;
		GdiplusStartup(&token, &gsi, NULL);
	}
} gdipinit;

LRESULT CALLBACK WndProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam);
void frame();
void draw_background();
void draw_border();
void draw_button();
void draw_label();
std::wstring to_hex(int num);

int num = 0;
HDC hdc;
HDC screenDC;
HDC memDC;
HDC labelDC;
int offset = 0;
HWND mhwnd;
Gdiplus::Graphics * graphics;
Graphics * glabel;
Color curColor;

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE prev, LPSTR cmdLine, int ncmdShow)
{
	XSCREEN = GetSystemMetrics(SM_CXSCREEN);
	YSCREEN = GetSystemMetrics(SM_CYSCREEN);

	WNDCLASSEX wc = { 0 };
	wc.cbSize = sizeof(WNDCLASSEX);
	wc.hbrBackground = NULL;
	wc.hCursor = LoadCursor(hInstance, IDC_ARROW);
	wc.hIcon = LoadIcon(hInstance, IDI_APPLICATION);
	wc.hIconSm = LoadIcon(hInstance, IDI_APPLICATION);
	wc.hInstance = hInstance;
	wc.lpszClassName = L"Window";
	wc.lpfnWndProc = WndProc;

	RegisterClassEx(&wc);

	HWND hwnd = CreateWindowEx(WS_EX_LAYERED, L"Window", L"straw", WS_POPUP, (XSCREEN - XWINDOW) / 2, (YSCREEN - YWINDOW) / 2, XWINDOW, YWINDOW, NULL, NULL, hInstance, NULL);

	UpdateWindow(hwnd);
	ShowWindow(hwnd, ncmdShow);

	mhwnd = hwnd;

	hdc = GetDC(hwnd);
	screenDC = GetWindowDC(NULL);
	memDC = CreateCompatibleDC(hdc);
	BITMAPINFO bmi = { sizeof(bmi),XWINDOW,YWINDOW,1,32, };
	//HBITMAP bmp = CreateCompatibleBitmap(hdc, XWINDOW, YWINDOW);
	HBITMAP bmp = CreateDIBSection(hdc, &bmi, DIB_RGB_COLORS, nullptr, nullptr, 0);
	SelectObject(memDC, bmp);
	DeleteObject(bmp);
	graphics = new Graphics(memDC);
	graphics->SetSmoothingMode(SmoothingMode::SmoothingModeAntiAlias);

	labelDC = CreateCompatibleDC(hdc);
	HBITMAP bmp2 = CreateCompatibleBitmap(hdc, XWINDOW, 1000);
	SelectObject(labelDC, bmp2);
	DeleteObject(bmp2);
	glabel = new Graphics(labelDC);
	glabel->Clear(BACKCOLOR);
	glabel->SetSmoothingMode(SmoothingMode::SmoothingModeAntiAlias);
	glabel->SetTextRenderingHint(TextRenderingHint::TextRenderingHintAntiAlias);

	MSG msg;
	do
	{
		if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
		{
			TranslateMessage(&msg);
			DispatchMessage(&msg);
		}
		else
		{
			frame();
		}
	} while (msg.message != WM_QUIT);

	ReleaseDC(hwnd, hdc);
	ReleaseDC(NULL, screenDC);
	DeleteDC(memDC);
	DeleteDC(labelDC);
	delete graphics;
	delete glabel;

	return 0;
}

LRESULT CALLBACK WndProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam)
{
	switch (Message)
	{
	case WM_DESTROY:
		PostQuitMessage(0);
		break;
	case WM_MOUSEWHEEL:
		int zDelta;
		zDelta = GET_WHEEL_DELTA_WPARAM(wParam);
		if (zDelta > 0)
		{
			offset = offset == 0 ? 0 : offset - 1;
		}
		else if (zDelta < 0)
		{
			offset++;
		}
		break;
	default:
		return DefWindowProc(hwnd, Message, wParam, lParam);
	}

	return 0;
}

void frame()
{
	static POINT lastpt = { 0,0 };
	static BOOL lastspace = FALSE;
	static BOOL lastlbutton = FALSE;
	BOOL curspace;
	BOOL curlbutton;
	POINT curpt;
	POINT clientpt;

	static int ox, oy;
	static RECT lastrc;
	static bool can_move = false;

	HWND fore = GetForegroundWindow();
	if (fore != mhwnd)
	{
		SetWindowPos(mhwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
	}

	//acquire the position of the mouse
	GetCursorPos(&curpt);																
	clientpt = curpt;
	ScreenToClient(mhwnd, &clientpt);

	curlbutton = GetAsyncKeyState(VK_LBUTTON) < 0;

	//left button down
	if (curlbutton && !lastlbutton)														
	{
		RECT rc;
		GetWindowRect(mhwnd, &rc);
		if (PtInRect(&rc, curpt))
		{
			ox = curpt.x;
			oy = curpt.y;
			GetWindowRect(mhwnd, &lastrc);
			can_move = true;
			//close button
			if (sqrt(pow(clientpt.x - XWINDOW, 2) + pow(clientpt.y, 2)) < BUTTONWIDTH)
			{
				DestroyWindow(mhwnd);
			}
		}
	}
	else if (!curlbutton && lastlbutton)
	{
		can_move = false;
	}
	lastlbutton = curlbutton;

	//mouse moves, should change color
	if (lastpt.x != curpt.x || lastpt.y != curpt.y)								
	{
		curColor.SetFromCOLORREF(GetPixel(screenDC, curpt.x, curpt.y));
		if (GetAsyncKeyState(VK_LBUTTON) < 0)
		{
			if (can_move)
			{
				SetWindowPos(mhwnd, HWND_NOTOPMOST, lastrc.left + curpt.x - ox, lastrc.top + curpt.y - oy, 0, 0, SWP_NOSIZE);
			}
		}
	}
	lastpt = curpt;

	curspace = GetAsyncKeyState(VK_SPACE) < 0;

	//space down, should record color
	if (curspace && !lastspace)
	{
		//draw a new label
		draw_label();	
		//the number of the labels ++
		num++;																			
	}
	lastspace = curspace;

	//draw
	draw_background();
	BitBlt(memDC, 0, y, XWINDOW, YWINDOW - 100, labelDC, 0, offset * 20, SRCCOPY);
	draw_border();
	draw_button();

	TCHAR temp[50];
	wsprintf(temp, L"%d,%d,%d", curpt.x, curpt.y, curColor);
	
	POINT pt = { (XSCREEN - XWINDOW) / 2,(YSCREEN - YWINDOW) / 2 };
	SIZE size = { XWINDOW,YWINDOW };
	BLENDFUNCTION bf = { 0,0,MAXBYTE,AC_SRC_ALPHA, };
	UpdateLayeredWindow(mhwnd, hdc, NULL, &size, memDC, &POINT(), 0, &bf, ULW_ALPHA);
}

// draw background
void draw_background()
{
	graphics->Clear(BACKCOLOR);
}

void draw_border()
{
	static const int ncHeight = 37;
	static SolidBrush brush{ 0xff000000 };
	static Pen pen{ 0xff000000 };
	static FontFamily ffm{ L"Consolas" };
	static Font font{ &ffm,11 };
	static Font font2{ &ffm,8 };

	brush.SetColor(curColor);
	pen.SetColor(curColor);

	graphics->FillRectangle(&brush, 0, -1, XWINDOW, ncHeight);
	graphics->DrawRectangle(&pen, 0, ncHeight - 1, XWINDOW - 1, YWINDOW - ncHeight);

	wchar_t temp[50];
	POINT pt;
	GetCursorPos(&pt);
	wsprintf(temp, L"mouse at: (%d,%d)\nRGB(%d,%d,%d)", pt.x, pt.y, curColor.GetR(), curColor.GetG(), curColor.GetB());
	brush.SetColor(0xffffffff);
	graphics->DrawString(L"straw", -1, &font, PointF(5, 6), &brush);
	graphics->DrawString(temp, -1, &font2, PointF(105, 2.5), &brush);
}

// draw the close button
void draw_button()
{
	static SolidBrush brush{ 0xff000000 };

	brush.SetColor(Color(255 - curColor.GetR(), 255 - curColor.GetG(), 255 - curColor.GetB()));
	graphics->FillPie(&brush, XWINDOW - BUTTONWIDTH, -BUTTONWIDTH - 1, 2*BUTTONWIDTH, 2*BUTTONWIDTH, 90, 90);
}

// draw color labels
void draw_label()
{
	static const int radius_color = 25;
	static const int radius_main = 20;
	static const int x = 40;
	static const int gap = 60;
	static SolidBrush brush{ 0xffffffff };
	static FontFamily ffm{ L"Consolas" };
	static Font font{ &ffm,9 };

	brush.SetColor(Color(curColor.GetR(), 0, 0));
	glabel->FillPie(&brush, x, num * gap, 2 * radius_color, 2 * radius_color, 0, 120);
	brush.SetColor(Color(0, curColor.GetG(), 0));
	glabel->FillPie(&brush, x, num * gap, 2 * radius_color, 2 * radius_color, 120, 120);
	brush.SetColor(Color(0, 0, curColor.GetB()));
	glabel->FillPie(&brush, x, num * gap, 2 * radius_color, 2 * radius_color, 240, 120);

	brush.SetColor(Color(curColor));
	glabel->FillEllipse(&brush, x + radius_color - radius_main, num * gap + radius_color - radius_main, 2 * radius_main, 2 * radius_main);

	wchar_t temp[50];
	wsprintf(temp, L"R:%d\tG:%d\tB:%d", curColor.GetR(), curColor.GetG(), curColor.GetB());
	brush.SetColor(0xffffffff);
	glabel->DrawString(temp, -1, &font, PointF(x + 85, num * gap + 8), &brush);
	std::wstring hex = L"0x";
	hex.append(to_hex(curColor.GetR()));
	hex.append(to_hex(curColor.GetG()));
	hex.append(to_hex(curColor.GetB()));
	glabel->DrawString(hex.c_str(), -1, &font, PointF(x + 85, num * gap + 25), &brush);
}

// convert num to hex number represented by a wstring
std::wstring to_hex(int num)
{
	static wchar_t code[] = L"0123456789ABCDEF";
	std::wstring temp = L"xx";

	for (int i = 0; i < 2; i++)
	{
		temp[1 - i] = code[num % 16];
		num /= 16;
	}

	return temp;
}